package com.hns.tool.pub;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * UF日期
 * @author:Fisher
 * @email:zhuangcaijin@126.com
 * @version Revision 2.0.0
 */
public final class UFDate implements java.io.Serializable, Comparable {
	static final long serialVersionUID = -1037968151602108293L;

	/**
	 * 日期字符串
	 */
	private String value = null;

	/**
	 * 一天内毫秒
	 */
	private static final long millisPerDay = 24 * 60 * 60 * 1000;

	private static final int LRUSIZE = 100 * 5;

	private static class LRUMap<K, V> extends LinkedHashMap<K, V> {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public LRUMap(int initSize) {
			super(initSize, 0.75f, true);
		}

		protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
			if (size() > LRUSIZE)
				return true;
			else
				return false;
		}
	}

	/**
	 * UF日期map
	 */
	private static Map<String, UFDate> allUsedDate = new LRUMap<String, UFDate>(
			100);

	/**
	 * 读写锁
	 */
	private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

	/**
	 * 当前毫秒
	 */
	private transient Long currentLong = null;

	/**
	 * 构造
	 * @email:zhuangcaijin@126.com
	 */
	public UFDate() {
		this(new Date());
	}

	/**
	 * 以从1970年1月1日0时0分0秒到现在的毫秒数来构造日期
	 * @param m 毫秒数
	 * @email:zhuangcaijin@126.com
	 */
	public UFDate(long m) {
		this(new java.util.Date(m));
	}

	/**
	 * 构造,用yyyy-MM-dd形式的字符串构造日期类型
	 * @param strDate 日期字符串
	 * @email:zhuangcaijin@126.com
	 */
	public UFDate(String strDate) {
		this(strDate, true);
	}

	/**
	 * 构造,如果解析，用yyyy-MM-dd形式的字符串构造日期类型 如果不解析，可能是yyyy，yyyy-MM 创建日期：(2001-9-18
	 * 10:03:53)
	 * @param strDate 日期字符串
	 * @param isParse 是否解析
	 * @email:zhuangcaijin@126.com
	 */
	public UFDate(String strDate, boolean isParse) {
		if (isParse) {
			value = internalParse(strDate);
		} else {
			if (strDate == null && strDate.trim().length() != 10) {
				throw new IllegalArgumentException("invalid UFDate:" + strDate);
			}
			value = strDate.trim();
		}
	}

	/**
	 * 构造,用java.sql.Date类型构造UF日期类型
	 * @param date 传入的java.sql.Date类型对象
	 * @email:zhuangcaijin@126.com
	 */
	public UFDate(java.sql.Date date) {
		this((java.util.Date) date);
	}

	/**
	 * 构造,用java.util.Date类型构造日期类型
	 * @param date 传入的java.sql.Date类型对象
	 * @email:zhuangcaijin@126.com
	 */
	public UFDate(java.util.Date date) {
		value = toDateString(date);
	}

	/**
	 * 比较日期先后，对象日期在参数日期之后为true
	 * @param when 参数日期
	 * @return 当前日期是否在参数日期之后
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public boolean after(UFDate when) {
		return this.compareTo(when) > 0;
	}

	/**
	 * 比较日期先后，对象日期在参数日期之前为true
	 * @param when 参数日期
	 * @return 当前日期是否在参数日期之前
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public boolean before(UFDate when) {
		return this.compareTo(when) < 0;
	}

	/**
	 * 克隆日期兑对象。
	 * @email:zhuangcaijin@126.com
	 */
	public Object clone() {
		return new UFDate(value);
	}

	/**
	 * 传入日期和当前日期进行比较先后
	 * @param when 传入的日期
	 * @return 当前日期和传入日期比较结果 大于0 ---为参数之后日期 等于0 ---和参数为同一天 小于0 ---为参数之前日期
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public int compareTo(UFDate when) {
		return compareTo(when.getMillis());
	}

	/**
	 * 传入日期毫秒和当前日期毫秒进行比较先后
	 * @param whenLong 传入的日期毫秒数
	 * @return 当前日期毫秒和传入日期毫秒比较结果 大于0 ---为参数之后日期 等于0 ---和参数为同一天 小于0 ---为参数之前日期
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	private int compareTo(Long whenLong) {
		long retl = this.getMillis() - whenLong;
		if (retl == 0)
			return 0;
		else
			return retl > 0 ? 1 : -1;
	}

	/**
	 * 比较日期先后，true为同一天
	 * @email:zhuangcaijin@126.com
	 */
	public boolean equals(Object o) {
		if ((o != null) && (o instanceof UFDate)) {
			return this.getMillis() == ((UFDate) o).getMillis();
		}
		return false;
	}

	/**
	 * 根据指定日期字符串,得到格式化日期
	 * @param strDate 日期字符串
	 * @return 格式化日期
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static UFDate getDate(String strDate) {
		if (strDate == null || strDate.trim().length() == 0)
			return null;
		return getDate(strDate, false);
	}

	/**
	 * 根据指定日期,得到格式化日期
	 * @param date 日期
	 * @return 格式化日期
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static UFDate getDate(Date date) {
		String strDate = toDateString(date);
		return getDate(strDate, false);
	}

	/**
	 * 根据指定日期字符串,得到格式化日期
	 * @param date 日期字符串
	 * @param check 是否解析
	 * @return 格式化日期
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static UFDate getDate(String date, boolean check) {
		rwl.readLock().lock();
		try {
			UFDate o = (UFDate) allUsedDate.get(date);
			if (o == null) {
				rwl.readLock().unlock();
				rwl.writeLock().lock();
				try {
					o = allUsedDate.get(date);
					if (o == null) {
						o = new UFDate(date, check);
						allUsedDate.put(date, o);
					}
				} finally {
					rwl.readLock().lock();
					rwl.writeLock().unlock();
				}
			}
			return o;
		} finally {
			rwl.readLock().unlock();
		}
	}

	/**
	 * 返回天数后的日期。
	 * @param days 天数
	 * @return 日期
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public UFDate getDateAfter(int days) {
		long l = getMillis() + millisPerDay * days;
		Date date = new Date(l);
		return getDate(date);

	}

	/**
	 * 返回天数前的日期。
	 * @param days 天数
	 * @return 日期
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public UFDate getDateBefore(int days) {
		return getDateAfter(-days);
	}

	/**
	 * 截取日期字符串(YYYY-MM-DD),获得日
	 * @return 日字符串
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public int getDay() {
		return Integer.parseInt(value.substring(8, 10));
	}

	/**
	 * 返回某一日期距今天数，负数表示在今天之后
	 * @param when 日期
	 * @return 距离今天天数
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public int getDaysAfter(UFDate when) {
		int days = 0;
		if (when != null) {
			days = (int) ((this.getMillis() - when.getMillis()) / millisPerDay);
		}
		return days;
	}

	/**
	 * 返回后一日期距前一日期之后后的天数
	 * @param begin 前一个日期
	 * @param end 后一个日期
	 * @return 两个日期间天数
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static int getDaysBetween(UFDate begin, UFDate end) {
		int days = 0;
		if (begin != null && end != null) {
			days = (int) ((end.getMillis() - begin.getMillis()) / millisPerDay);
		}
		return days;
	}

	/**
	 * 得到某年的某一个月中天数
	 * @return 天数
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public int getDaysMonth() {
		return getDaysMonth(getYear(), getMonth());
	}

	/**
	 * 得到某年的某一个月中天数
	 * @param year 年
	 * @param month 月
	 * @return 
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static int getDaysMonth(int year, int month) {
		switch (month) {
		case 1:
			return 31;
		case 2:
			if (isLeapYear(year))
				return 29;
			else
				return 28;
		case 3:
			return 31;
		case 4:
			return 30;
		case 5:
			return 31;
		case 6:
			return 30;
		case 7:
			return 31;
		case 8:
			return 31;
		case 9:
			return 30;
		case 10:
			return 31;
		case 11:
			return 30;
		case 12:
			return 31;
		default:
			return 30;
		}
	}

	/**
	 * 得到英语月份
	 * @return 英文某月字符串
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public String getEnMonth() {
		switch (getMonth()) {
		case 1:
			return "Jan";
		case 2:
			return "Feb";
		case 3:
			return "Mar";
		case 4:
			return "Apr";
		case 5:
			return "May";
		case 6:
			return "Jun";
		case 7:
			return "Jul";
		case 8:
			return "Aug";
		case 9:
			return "Sep";
		case 10:
			return "Oct";
		case 11:
			return "Nov";
		case 12:
			return "Dec";
		}
		return null;
	}

	/**
	 * 得到星期几英文
	 * @return 星期几英文
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public String getEnWeek() {
		switch (getWeek()) {
		case 0:
			return "Sun";
		case 1:
			return "Mon";
		case 2:
			return "Tue";
		case 3:
			return "Wed";
		case 4:
			return "Thu";
		case 5:
			return "Fri";
		case 6:
			return "Sat";
		}
		return null;
	}

	/**
	 * 得到日期字符串(YYYY-MM-DD)中的月份
	 * @return 月份
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public int getMonth() {
		return Integer.parseInt(value.substring(5, 7));
	}

	/**
	 * 得到日期字符串(YYYY-MM-DD)中的日
	 * @return 日字符串
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public String getStrDay() {
		return value.substring(8, 10);
	}

	/**
	 * 得到日期字符串(YYYY-MM-DD)中的月
	 * @return 月字符串
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public String getStrMonth() {
		return value.substring(5, 7);
	}

	/**
	 * 获得今天星期几
	 * @return 代表星期几的整数
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public int getWeek() {
		int days = getDaysAfter(new UFDate("1980-01-06"));
		int week = days % 7;
		if (week < 0)
			week += 7;
		return week;
	}

	/**
	 * 获得年的数值
	 * @return 年份数值
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public int getYear() {
		return Integer.parseInt(value.substring(0, 4));
	}

	/**
	 * 是否闰年。
	 * @return 是否闰年。
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public boolean isLeapYear() {
		return isLeapYear(getYear());
	}

	/**
	 * 是否闰年。
	 * @param year 年数值
	 * @return 是否闰年。
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static boolean isLeapYear(int year) {
		if ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0))
			return true;
		else
			return false;
	}

	/**
	 * 转为字符串
	 * @email:zhuangcaijin@126.com
	 */
	public String toString() {
		return value == null ? "" : value;
	}

	/**
	 * 比较对象
	 * @email:zhuangcaijin@126.com
	 */
	public int compareTo(Object o) {
		if (o instanceof UFDate)
			return compareTo((UFDate) o);
		else if (o instanceof UFDateTime)
			return compareTo(((UFDateTime) o).getMillis());
		else
			throw new IllegalArgumentException();
	}

	/**
	 * 当前毫秒数
	 * @return 毫秒数
	 * @author:Fisher
	 * @email:zhuangcaijin@126.com
	 */
	public long getMillis() {
		if (currentLong == null) {
			currentLong = new java.util.GregorianCalendar(getYear(),
					getMonth() - 1, getDay()).getTimeInMillis();
		}
		return currentLong;
	}

	/**
	 * 返加当前日期在一年内的周数。
	 * @return 
	 * @author:Fisher
	 * @email:zhuangcaijin@126.com
	 */
	public int getWeekOfYear() {
		GregorianCalendar calendar = new GregorianCalendar(getYear(),
				getMonth(), getDay());
		return calendar.get(Calendar.WEEK_OF_YEAR);
	}

	/**
	 * 毫秒数转为日期
	 * @return 日期
	 * @author:Fisher
	 * @email:zhuangcaijin@126.com
	 */
	public Date toDate() {
		return new Date(getMillis());
	}

	/**
	 * 进行序列化增加修改
	 * @email:zhuangcaijin@126.com
	 */
	@Override
	public int hashCode() {
		return value == null ? 17 : value.hashCode();
	}

	/**
	 * 解析传入的日期字符串,并生成指定格式(YYYY-MM-DD)的日期字符串
	 * @param sDate 传入的字符串数组
	 * @return 生成的日期字符串(YYYY-MM-DD)
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	private static String internalParse(String sDate) {

		if (sDate == null)
			throw new IllegalArgumentException("invalid UFDate: " + sDate);

		sDate = sDate.trim();
		String[] tokens = new String[3];

		StringTokenizer st = new StringTokenizer(sDate, "-/");

		if (st.countTokens() != 3) {
			throw new IllegalArgumentException("invalid UFDate: " + sDate);
		}

		int i = 0;
		while (st.hasMoreTokens()) {
			tokens[i++] = st.nextToken().trim();
		}

		try {
			int year = Integer.parseInt(tokens[0]);
			int month = Integer.parseInt(tokens[1]);
			if (month < 1 || month > 12)
				throw new IllegalArgumentException("invalid UFDate: " + sDate);
			int day = Integer.parseInt(tokens[2]);

			int MONTH_LENGTH[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
					31 };
			int LEAP_MONTH_LENGTH[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31,
					30, 31 };
			int daymax = isLeapYear(year) ? LEAP_MONTH_LENGTH[month - 1]
					: MONTH_LENGTH[month - 1];

			if (day < 1 || day > daymax)
				throw new IllegalArgumentException("invalid ufdate: " + sDate);

			String strYear = tokens[0];
			for (int j = strYear.length(); j < 4; j++) {
				if (j == 3) {
					strYear = "2" + strYear;
				} else {
					strYear = "0" + strYear;
				}
			}

			String strMonth = String.valueOf(month);
			if (strMonth.length() < 2)
				strMonth = "0" + strMonth;
			String strDay = String.valueOf(day);
			if (strDay.length() < 2)
				strDay = "0" + strDay;
			return strYear + "-" + strMonth + "-" + strDay;
		} catch (Throwable thr) {
			if (thr instanceof IllegalArgumentException) {
				throw (IllegalArgumentException) thr;
			} else {
				throw new IllegalArgumentException("invalid ufdate: " + sDate);
			}
		}

	}

	/**
	 * 根据传入的年月日数值,拼接为年月日(YYYY-MM-DD)字符串
	 * @param year 年
	 * @param month 月
	 * @param day 日
	 * @return 年月日字符串
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	private static String toDateString(int year, int month, int day) {
		String strYear = String.valueOf(year);
		for (int j = strYear.length(); j < 4; j++)
			strYear = "0" + strYear;
		String strMonth = String.valueOf(month);
		if (strMonth.length() < 2)
			strMonth = "0" + strMonth;
		String strDay = String.valueOf(day);
		if (strDay.length() < 2)
			strDay = "0" + strDay;
		return strYear + "-" + strMonth + "-" + strDay;

	}

	/**
	 * 日期转日期字符串
	 * @param dt 日期
	 * @return 日期字符串
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	private static String toDateString(Date dt) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(dt);
		return toDateString(cal.get(Calendar.YEAR),
				cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH));
	}

	/**
	 * 如果字符串的日期能转换成有效日期串－－－转换。 /n 创建日期：(2001-5-28 13:28:29) UIBean使用
	 * @param sDate 日期字符串
	 * @return 生成的日期字符串(YYYY-MM-DD)
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static String getValidUFDateString(String sDate) {
		return internalParse(sDate);
	}

	/**
	 * 如果字符串能转换成日期返回true。
	 * @param strDate 日期字符串
	 * @return 转换是否成功
	 * @author:Fisher
     * @email:zhuangcaijin@126.com
	 */
	public static boolean isAllowDate(String strDate) {
		try {
			internalParse(strDate);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

}